/*
 * UmsgMediaCasterServer.cpp
 *
 *  Created on: 2016年7月15日
 *      Author: terry
 */

#include "UmsgMediaCasterServer.h"
#include "CLog.h"
#include <functional>

#ifdef WIN32
#include <Ws2tcpip.h>
#endif //

#include <evutil.h>

namespace av
{

void UmsgMediaCasterServer::Startup()
{
	UDT::startup();
}

void UmsgMediaCasterServer::Cleanup()
{
	UDT::cleanup();
}



UmsgMediaCasterServer::UmsgMediaCasterServer():
		m_port(),
		m_socket(UDT::INVALID_SOCK),
		m_epoll()
{
}

UmsgMediaCasterServer::~UmsgMediaCasterServer()
{
	stop();
}

bool UmsgMediaCasterServer::start(const char* ip, int port, const std::string& params)
{
	m_socket = UDT::socket(AF_INET, SOCK_DGRAM, 0);
	if (m_socket == UDT::INVALID_SOCK)
	{
		return false;
	}

	m_port = port;

	sockaddr_in my_addr;
	my_addr.sin_family = AF_INET;
	my_addr.sin_port = htons(port);
	my_addr.sin_addr.s_addr = INADDR_ANY;
	memset(&(my_addr.sin_zero), '\0', 8);

	if (UDT::ERROR == UDT::bind(m_socket, (sockaddr*)&my_addr, sizeof(my_addr)))
	{
		CLog::warning("bind: %s", UDT::getlasterror().getErrorMessage());
		UDT::close(m_socket);
		m_socket = UDT::INVALID_SOCK;
		return 0;
	}

	UDT::listen(m_socket, 10);

	m_epoll = UDT::epoll_create();
	UDT::epoll_add_usock(m_epoll, m_socket);

	return comn::Thread::start();
}

bool UmsgMediaCasterServer::isStarted()
{
	return isRunning();
}

void UmsgMediaCasterServer::stop()
{
	if (isRunning())
	{
		comn::Thread::stop();
	}

	if (m_socket != UDT::INVALID_SOCK)
	{
		UDT::close(m_socket);
		m_socket = UDT::INVALID_SOCK;
	}

	closeAll();
}


struct EqualToName : public std::binary_function< UDTSOCKET, UmsgMediaCasterConnectionPtr, bool >
{
    EqualToName(const char* name):
        m_name(name)
    {
    }

	bool operator () (UDTSOCKET fd, UmsgMediaCasterConnectionPtr& conn) const
	{
		return conn->equals(m_name);
	}

    std::string m_name;
};

void UmsgMediaCasterServer::close(const char* name)
{
	UDTSOCKET fd = UDT::INVALID_SOCK;
	UmsgMediaCasterConnectionPtr conn;
	m_connMap.removeIf(EqualToName(name), fd, conn);
	if (conn)
	{
		conn->close();
	}
}

void UmsgMediaCasterServer::closeAll()
{
	UDTSOCKET fd = UDT::INVALID_SOCK;
	UmsgMediaCasterConnectionPtr conn;
	while (m_connMap.pop_front(fd, conn))
	{
		conn->close();
	}
}

int UmsgMediaCasterServer::run()
{
	while (!m_canExit)
	{
		UDTSOCKET fds = UDT::INVALID_SOCK;
		int rnum = 1;
        int ret = UDT::epoll_wait2(m_epoll, &fds, &rnum, NULL, 0, -1);
		if ((fds == m_socket) & !m_canExit)
		{
			int namelen;
			sockaddr_in their_addr;

			UDTSOCKET recver = UDT::accept(m_socket, (sockaddr*)&their_addr, &namelen);

			UmsgMediaCasterConnectionPtr conn(new UmsgMediaCasterConnection());
			if (conn->open(this, recver))
			{
				m_connMap.put(recver, conn);
			}

			CLog::debug("new umsg connection. %s:%d\n", inet_ntoa(their_addr.sin_addr), ntohs(their_addr.sin_port));
		}
	}
	return 0;
}

void UmsgMediaCasterServer::doStop()
{
	UDTSOCKET client = UDT::socket(AF_INET, SOCK_DGRAM, 0);

	sockaddr_in serv_addr;
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(m_port);

#if defined(_MSC_VER)
	evutil_inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr);
#else
	inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr);
#endif

	memset(&(serv_addr.sin_zero), '\0', 8);

	UDT::connect(client, (sockaddr*)&serv_addr, sizeof(serv_addr));

	UDT::close(client);
}


void UmsgMediaCasterServer::onAcquireStream(const std::string& name, UmsgMediaCasterConnectionPtr& conn)
{
    sockaddr_in addr = conn->getPeerName();

    MediaCasterEvent event;
    event.type = kCaster_acquire;
    event.ip = inet_ntoa(addr.sin_addr);
    event.port = ntohs(addr.sin_port);
    event.streamName = name;

    fireEvent(event, kUdtCaster);
}

void UmsgMediaCasterServer::remove(UmsgMediaCasterConnectionPtr& conn)
{
    onClose(conn);

	m_connMap.remove(conn->getHandle());
}


void UmsgMediaCasterServer::onClose(UmsgMediaCasterConnectionPtr& conn)
{
    sockaddr_in addr = conn->getPeerName();

    MediaCasterEvent event;
    event.type = kCaster_destroy;
    event.ip = inet_ntoa(addr.sin_addr);
    event.port = ntohs(addr.sin_port);
    event.streamName = conn->getStreamName();

    fireEvent(event, kUdtCaster);

}



} /* namespace av */
